/**************************************************************************************************
* Filename:			DirectInput.h
* Date:				09/13/10
* Mod. Date:		09/13/10
* Mod. Initials:	MG
* Author:			Malcolm J. Gruber
* Purpose:			An outlying wrapper for Direct Input for keyboard and mouse.
**************************************************************************************************/

#ifndef _DIRECTINPUT_H_
#define _DIRECTINPUT_H_

#ifndef DIRECTINPUT_VERSION
	#define DIRECTINPUT_VERSION DIRECTINPUT_HEADER_VERSION
#endif

#include <stdint.h>
#include <dinput.h>
#include "../Generic/Utility.h"

#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")

const int g_nBufferSize = 10;

class DirectKeyboard;
class DirectMouse;

#define		DEFAULT_MOUSE_SENSITIVITY			100
#define		MAX_MOUSE_SENSITIVITY					200

#ifndef DIERRMSG
	#define DIERRMSG(hWnd, errMsg) { MessageBoxA(hWnd, errMsg, "Direct Input Error", 0); }
#endif // DIERRMSG

enum DIFlags { DI_KEYBOARD = 1, DI_MOUSE = 2 };
enum DIMOUSE { DIMOUSE_LEFTBUTTON = 0, DIMOUSE_RIGHTBUTTON, DIMOUSE_MIDDLEBUTTON, 
	DIMOUSE_4BUTTON, DIMOUSE_5BUTTON, DIMOUSE_6BUTTON, DIMOUSE_7BUTTON, DIMOUSE_8BUTTON };

class DirectInput
{
private:
	LPDIRECTINPUT8		m_lpDICom;

	DirectKeyboard*		m_pKeyboard;
	DirectMouse*		m_pMouse;

	static DirectInput	m_Instance;

private:
	DirectInput(void);
	DirectInput(const DirectInput&);
	DirectInput& operator =(const DirectInput&);
	
	~DirectInput(void);

	bool InitKeyboard(HWND hWnd, bool bExclusive = false);	
	bool InitMouse(HWND hWnd, bool bExclusive = false);

public:
	static DirectInput* GetInstance(void);

	bool InitDirectInput(HWND hWnd, HINSTANCE hInstance, uint32_t unDevices, uint32_t unExclusive = 0);
	void ShutdownDirectInput(void);

	void ReadDevices(void);
	void ClearInput(void);
	void AcquireAll(void);
	void UnacquireAll(void);

	bool KeyDown(unsigned char ucDIKey);
	bool KeyPressed(unsigned char ucDIKey);
	bool KeyUp(unsigned char ucDIKey);
	bool KeyReleased(unsigned char ucDIKey);
	bool KeyPressedEx(unsigned char ucDIKey);
	bool KeyReleasedEx(unsigned char ucDIKey);
	uint16_t CheckKeys(void);
	uint16_t CheckKeysEx(void);
	int32_t CheckMouseButton(void);
	int32_t CheckMouseButtonEx(void);

	int16_t GetDIKCode(void);
	int16_t GetDIKCodeEx(void);

	bool MouseButtonDown(unsigned char ucDIButton);
	bool MouseButtonPressed(unsigned char ucDIButton);
	bool MouseButtonUp(unsigned char ucDIButton);
	bool MouseButtonReleased(unsigned char ucDIButton);
	bool MouseButtonPressedEx(unsigned char ucDIButton);
	bool MouseButtonReleasedEx(unsigned char ucDIButton);
	int32_t MouseSpeedX(void);
	int32_t MouseSpeedY(void);
	int32_t MouseSpeedWheel(void);
	int32_t GetNumMouseButtons(void);
	int32_t GetMousePosX(void);
	int32_t GetMousePosY(void);
	void SetMousePosX(int32_t nPosX);
	void SetMousePosY(int32_t nPosY);
	void SetMouseSensitivity(int32_t nSensValue);
	int32_t GetMouseSensitivity(void);
};

class IDIDevice
{
public:
	virtual ~IDIDevice(void) = 0 {}

	virtual bool ReadDevice(void) = 0;
	virtual bool ReadBufferedDevice(void) = 0;

	virtual bool Acquire(void) = 0;
	virtual bool Unacquire(void) = 0;
};

class DirectInputDevice : public IDIDevice
{
protected:
	LPDIRECTINPUTDEVICE8	m_lpDevice;

	DIDEVICEOBJECTDATA		m_dodBuffer[g_nBufferSize];
	DWORD					m_dwElementBuffer;

	void ClearDataBuffer(void)
	{
		memset(&m_dodBuffer, 0, sizeof(m_dodBuffer));
		m_dwElementBuffer = g_nBufferSize;
	}

public:

	DirectInputDevice(void)
	{
		m_lpDevice = NULL;
		ClearDataBuffer();
	}

	virtual ~DirectInputDevice(void)
	{
		if( m_lpDevice )
		{
			m_lpDevice->Unacquire();
			SAFE_RELEASE(m_lpDevice);
		}
	}

	virtual bool ReadBufferedDevice(void)
	{
		if( !m_lpDevice )
			return false;

		ClearDataBuffer();

		if( FAILED( m_lpDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_dodBuffer, &m_dwElementBuffer, 0) ))
		{
			if( FAILED( m_lpDevice->Acquire() ))
				return false;

			ClearDataBuffer();

			if( FAILED( m_lpDevice->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), m_dodBuffer, &m_dwElementBuffer, 0) ))
				return false;
		}

		return true;
	}

	virtual bool Acquire(void)
	{
		return m_lpDevice ? SUCCEEDED( m_lpDevice->Acquire() ) : false;
	}

	virtual bool Unacquire(void)
	{
		return m_lpDevice ? SUCCEEDED( m_lpDevice->Unacquire() ) : false;
	}
};


class DirectKeyboard : public DirectInputDevice
{
private:
	unsigned char	m_ucKeysPressed[256];
	unsigned char	m_ucPrevKeysPressed[256];

	unsigned char	m_ucAsciiVals[256];
	HKL				m_keyLayout;

public:
	void ClearKeys(void)
	{
		memset(m_ucKeysPressed, 0, 256 * sizeof(unsigned char));
		memset(m_ucPrevKeysPressed, 0, 256 * sizeof(unsigned char));
	}

	DirectKeyboard(LPDIRECTINPUT8 lpDI, HWND hWnd, bool bExclusive=false);

	bool ReadDevice(void);
	bool ReadBufferedDevice(void);
	
	bool Acquire(void)
	{
		ClearKeys();
		return DirectInputDevice::Acquire();
	}

	bool Unacquire(void)
	{
		ClearKeys();
		return DirectInputDevice::Unacquire();
	}
	
	bool KeyDown(unsigned char ucKey);
	bool KeyPressed(unsigned char ucKey);
	bool KeyUp(unsigned char ucKey);
	bool KeyReleased(unsigned char ucKey);
	bool KeyPressedEx(unsigned char ucKey);
	bool KeyReleasedEx(unsigned char ucKey);
	
	unsigned char CheckKeys(void);
	unsigned char CheckKeysEx(void);
	int8_t GetDIKCode(void);
	int8_t GetDIKCodeEx(void);
};


class DirectMouse : public DirectInputDevice
{
private:
	DIMOUSESTATE2	m_diMouseState;
	DIMOUSESTATE2	m_diPrevMouseState;

	int32_t			m_nNumButtons;

	int32_t			m_nPosX;
	int32_t			m_nPosY;
	int32_t			m_nSensitivity;

public:
	void ClearMouseButtons(void)
	{
		memset(m_diMouseState.rgbButtons, 0, 8*sizeof(int8_t));
		memset(m_diPrevMouseState.rgbButtons, 0, 8*sizeof(int8_t));
	}

	DirectMouse(LPDIRECTINPUT8 lpDI, HWND hWnd, bool bExclusive=false);

	bool ReadDevice(void);
	bool ReadBufferedDevice(void);

	bool ButtonDown(unsigned char ucButton);
	bool ButtonPressed(unsigned char ucButton);
	bool ButtonUp(unsigned char ucButton);
	bool ButtonReleased(unsigned char ucButton);

	bool ButtonPressedEx(unsigned char ucButton);
	bool ButtonReleasedEx(unsigned char ucButton);

	int32_t CheckMouseButtons(void);
	int32_t CheckMouseButtonsEx(void);
	int32_t MouseSpeedX(void);
	int32_t MouseSpeedY(void);
	int32_t WheelSpeed(void);
	int32_t GetNumMouseButtons(void);
	int32_t GetMousePosX(void);
	int32_t GetMousePosY(void);
	void SetMousePosX(int32_t nPosX);
	void SetMousePosY(int32_t nPosY);
	void SetMouseSensitivity(int32_t nSensitivity);
	int32_t GetMouseSensitivity(void);
};

#endif // _DirectINPUT_H_